package server

import (
  "bufio"
  "errors"
  "io"
  "net"
  "runtime"
  "strconv"
  "strings"

  "github.com/siddontang/go-log/log"
)

var errReadRequest = errors.New("invalid request protocol")

type respClient struct {
  app *App

  conn net.Conn
  rb   *bufio.Reader

  req *requestContext
}

type respWriter struct {
  buff *bufio.Writer
}

func newClientRESP(conn net.Conn, app *App) {
  c := new(respClient)

  c.app = app
  c.conn = conn

  c.rb = bufio.NewReaderSize(conn, 256)

  c.req = newRequestContext(app)
  c.req.resp = newWriterRESP(conn)
  c.req.remoteAddr = conn.RemoteAddr().String()

  go c.run()
}

func (c *respClient) run() {
  defer func() {
    if e := recover(); e != nil {
      buf := make([]byte, 4096)
      n := runtime.Stack(buf, false)
      buf = buf[0:n]

      log.Fatal("client run panic %s:%v", buf, e)
    }

    c.conn.Close()
  }()

  for {
    reqData, err := c.readRequest()
    if err != nil {
      return
    }

    c.handleRequest(reqData)
  }
}

func (c *respClient) readLine() ([]byte, error) {
  return ReadLine(c.rb)
}

//A client sends to the Redis server a RESP Array consisting of just Bulk Strings.
func (c *respClient) readRequest() ([][]byte, error) {
  l, err := c.readLine()
  if err != nil {
    return nil, err
  } else if len(l) == 0 || l[0] != '*' {
    return nil, errReadRequest
  }

  var nparams int
  if nparams, err = strconv.Atoi(string(l[1:])); err != nil {
    return nil, err
  } else if nparams <= 0 {
    return nil, errReadRequest
  }

  req := make([][]byte, 0, nparams)
  var n int
  for i := 0; i < nparams; i++ {
    if l, err = c.readLine(); err != nil {
      return nil, err
    }

    if len(l) == 0 {
      return nil, errReadRequest
    } else if l[0] == '$' {
      //handle resp string
      if n, err = strconv.Atoi(string(l[1:])); err != nil {
        return nil, err
      } else if n == -1 {
        req = append(req, nil)
      } else {
        buf := make([]byte, n)
        if _, err = io.ReadFull(c.rb, buf); err != nil {
          return nil, err
        }

        if l, err = c.readLine(); err != nil {
          return nil, err
        } else if len(l) != 0 {
          return nil, errors.New("bad bulk string format")
        }

        req = append(req, buf)

      }

    } else {
      return nil, errReadRequest
    }
  }

  return req, nil
}

func (c *respClient) handleRequest(reqData [][]byte) {
  if len(reqData) == 0 {
    c.req.cmd = ""
    c.req.args = reqData[0:0]
  } else {
    c.req.cmd = strings.ToLower(string(reqData[0]))
    c.req.args = reqData[1:]
  }
  if c.req.cmd == "quit" {
    c.req.resp.writeStatus(OK)
    c.req.resp.flush()
    c.conn.Close()
    return
  }

  c.req.perform()

  return
}

//	response writer

func newWriterRESP(conn net.Conn) *respWriter {
  w := new(respWriter)
  w.buff = bufio.NewWriterSize(conn, 256)
  return w
}

func (w *respWriter) writeBytes(b []byte) {
  w.buff.Write(b)
}

func (w *respWriter) writeError(err error) {
  // TODO
}

func (w *respWriter) writeStatus(status string) {
  // TODO
}

func (w *respWriter) writeInteger(n int64) {
  // TODO
}

func (w *respWriter) writeBulk(b []byte) {
  // TODO
}

func (w *respWriter) writeArray(lst []interface{}) {
  // TODO
}

func (w *respWriter) writeSliceArray(lst [][]byte) {
  // TODO
}

func (w *respWriter) writeBulkFrom(n int64, rb io.Reader) {
  // TODO
}

func (w *respWriter) flush() {
  w.buff.Flush()
}
